/**
 * @author hfutrell
 */

function Plane(n) {
	// n is a 4-vector
	this.n = n;
}

function Ray(o, d) {
	// o = origin, 3-tuple
	// d = direction, 3-tuple
	this.o = o;
	this.d = d;
}

function Camera() {
	
	// public member variables
	this.position 		= $V([0, 0, 0]);
	this.viewDirection 	= $V([0, 0, -1]);
	this.upVector 		= $V([0, 1, 0]);
	
	// set with makeOrtho or makeProjection
	this.projectionMatrix = null;
}

Camera.prototype.getModelViewMatrix = function() {
	
	// works like gluLookAt()
		
	var f 			= this.viewDirection.toUnitVector();
	var upPrime 	= this.upVector.toUnitVector();
	var s 			= f.cross(upPrime).toUnitVector();
	var u			= s.cross(f).toUnitVector();
	
	var entries1 = [
		[s.e(1), s.e(2), s.e(3), 0 ],
		[u.e(1), u.e(2), u.e(3), 0 ],
		[-f.e(1), -f.e(2), -f.e(3), 0 ],
		[0, 0, 0, 1 ],
	];
	
	var entries2 = [
		[1.0, 0.0, 0.0, -this.position.e(1)],
		[0.0, 1.0, 0.0, -this.position.e(2)],
		[0.0, 0.0, 1.0, -this.position.e(3)],
		[0.0, 0.0, 0.0, 1]
	];

	
	return $M(entries1).x($M(entries2));
	
};

/*
 *  unproject from ndc to object coordinates
 *  returns a Vector
 */
Camera.prototype.unProject = function(winX, winY, winZ, model, proj, view) {
	// model and proj are sylvester matrices
	// view is an array with four elements x,y, width, height
	// returns a sylvester vector
	var InvPM = (proj.x(model)).inverse();
	
	var resultVector = InvPM.x(
		$V([
			2.0 * ( winX - view[0] ) / view[2] - 1.0, 
			2.0 * ( winY - view[1] ) / view[3] - 1.0, 
			2.0 * winZ - 1.0, 
			1.0
		])
	);
	
	var w = resultVector.e(4);
	resultVector = resultVector.multiply(1.0 / w);
	
	return resultVector;
	
};
